package com.sojson.service.impl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sojson.config.exception.TransException;
import com.sojson.constant.Constant;
import com.sojson.dao.MenuDao;
import com.sojson.dao.RoleDao;
import com.sojson.dao.RoleMenuDao;
import com.sojson.entity.bo.MetaBo;
import com.sojson.entity.bo.RouterBo;
import com.sojson.entity.bo.TreeSelect;
import com.sojson.entity.po.Menu;
import com.sojson.entity.po.Role;
import com.sojson.entity.vo.MenuVo;
import com.sojson.enums.EEnvironmentUserType;
import com.sojson.enums.core.EMenuIsFrame;
import com.sojson.enums.core.EMenuType;
import com.sojson.service.MenuService;
import com.sojson.util.StringUtil;
import com.sojson.util.token.TokenUtil;

/**
 * 菜单权限表Service实现类
 * 
 * @author liu
 * @date 2021-04-13
 */
@Service
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuDao menuDaoImpl;
    @Resource
    private RoleDao roleDao;
    @Resource
    private RoleMenuDao roleMenuDao;

    /**
     * 单条插入
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int insert(Menu menu) throws SQLException {
        int insert = menuDaoImpl.insert(menu);
        return insert;
    }

    /**
     * 批量插入
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int insertList(Menu menu) throws SQLException {
        int insertList = menuDaoImpl.insertList(menu.getMenus());
        return insertList;
    }

    /**
     * 通过对象批量删除(物理删除)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int deleteByDto(Menu menu) throws SQLException {
        int deleteByDto = menuDaoImpl.deleteByDto(menu);
        return deleteByDto;
    }

    /**
     * 统计全部数量
     * 
     * @return
     * @throws SQLException
     */
    @Override
    public int countAll() throws SQLException {
        int countAll = menuDaoImpl.countAll();
        return countAll;
    }

    /**
     * 查询所有
     * 
     * @return
     * @throws SQLException
     */
    @Override
    public List<Menu> findAll() throws SQLException {
        List<Menu> findAll = menuDaoImpl.listAll();
        return findAll;
    }

    /**
     * 根据对象统计数量
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int countByDto(Menu menu) throws SQLException {
        int countByDto = menuDaoImpl.countByDto(menu);
        return countByDto;
    }

    /**
     * 根据对象查询
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public List<Menu> findByDto(Menu menu) throws SQLException {
        List<Menu> findByDto = menuDaoImpl.listByDto(menu);
        return findByDto;
    }

    /**
     * 通过id删除(物理删除)
     * 
     * @param id
     * @return
     * @throws SQLException
     */
    @Override
    @Transactional
    public int deleteById(Integer id) throws SQLException {
        int hasChildByMenuId = menuDaoImpl.hasChildByMenuId(id);
        if (hasChildByMenuId > 0) {
            throw new TransException("存在子菜单,不允许删除");
        }
        int checkMenuExistRole = roleMenuDao.checkMenuExistRole(id);
        if (checkMenuExistRole > 0) {
            throw new TransException("菜单已分配,不允许删除");
        }

        int deleteById = menuDaoImpl.deleteById(id);
        return deleteById;
    }

    /**
     * 通过id列表删除(物理删除)
     * 
     * @param ids
     * @return
     * @throws SQLException
     */
    @Override
    public int deleteByIdList(ArrayList<Integer> ids) throws SQLException {
        int deleteByIdList = menuDaoImpl.deleteByIdList(ids);
        return deleteByIdList;
    }

    /**
     * 通过id更新(全部更新)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updateById(Menu menu) throws SQLException {
        int updateById = menuDaoImpl.updateById(menu);
        return updateById;
    }

    /**
     * 通过id列表更新(全部更新)(更新内容相同)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updateStaticByIdList(Menu menu) throws SQLException {
        int updateStaticByIdList = menuDaoImpl.updateStaticByIdList(menu);
        return updateStaticByIdList;
    }

    /**
     * 通过id列表更新(全部更新)(更新内容不相同)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updateChangeByIdList(Menu menu) throws SQLException {
        int updateChangeByIdList = menuDaoImpl.updateChangeByIdList(menu.getMenus());
        return updateChangeByIdList;
    }

    /**
     * 通过id更新(局部更新)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updatePartialById(Menu menu) throws SQLException {
        int updatePartialById = menuDaoImpl.updatePartialById(menu);
        return updatePartialById;
    }

    /**
     * 通过id列表更新(局部更新)(更新内容相同)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updateStaticPartialByIdList(Menu menu) throws SQLException {
        int updateStaticPartialByIdList = menuDaoImpl.updateStaticPartialByIdList(menu);
        return updateStaticPartialByIdList;
    }

    /**
     * 通过id列表更新(局部更新)(更新内容不相同)
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public int updateChangePartialByIdList(Menu menu) throws SQLException {
        int updateChangePartialByIdList = menuDaoImpl.updateChangePartialByIdList(menu.getMenus());
        return updateChangePartialByIdList;
    }

    /**
     * 通过id查询
     * 
     * @param id
     * @return
     * @throws SQLException
     */
    @Override
    public Menu findById(Integer id) throws SQLException {
        Menu findById = menuDaoImpl.findById(id);
        return findById;
    }

    /**
     * 通过id列表查询
     * 
     * @param ids
     * @return
     * @throws SQLException
     */
    @Override
    public ArrayList<Menu> findByIdList(ArrayList<Integer> ids) throws SQLException {
        ArrayList<Menu> findByIdList = menuDaoImpl.findByIdList(ids);
        return findByIdList;
    }

    /**
     * 通过对象查询id列表
     * 
     * @param menu
     * @return
     * @throws SQLException
     */
    @Override
    public ArrayList<Integer> findIdByDto(Menu menu) throws SQLException {
        ArrayList<Integer> findIdByDto = menuDaoImpl.findIdByDto(menu);
        return findIdByDto;
    }

    /**
     * 清空数据(物理清空)
     * 
     * @throws SQLException
     */
    @Override
    public void clear() throws SQLException {
        menuDaoImpl.clear();
    }

    /**
     * 根据用户ID查询菜单
     *
     * @param id 用户名称
     * @return 菜单列表
     */
    @Override
    public List<Menu> getMenuTreeByUserId(MenuVo menu) {
        List<Menu> menus = null;
        if (TokenUtil.isAdmin(menu.getUserId())) {
            menu.setUserId(null);
            menus = menuDaoImpl.getMenuTreeAll(menu);
        } else {
            menus = menuDaoImpl.getMenuTreeByUserId(menu);
        }

        return getChildPerms(menus, 0);
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<Menu> getChildPerms(List<Menu> list, int parentId) {
        List<Menu> returnList = new ArrayList<>();
        for (Iterator<Menu> iterator = list.iterator(); iterator.hasNext();) {
            Menu t = iterator.next();
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getParentId() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<Menu> list, Menu t) {
        // 得到子节点列表
        List<Menu> childList = getChildList(list, t);
        t.setChildren(childList);
        for (Menu tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<Menu> getChildList(List<Menu> list, Menu t) {
        List<Menu> tlist = new ArrayList<>();
        Iterator<Menu> it = list.iterator();
        while (it.hasNext()) {
            Menu n = it.next();
            if (n.getParentId().intValue() == t.getId().intValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<Menu> list, Menu t) {
        return getChildList(list, t).size() > 0;
    }

    /**
     * 构建前端路由所需要的菜单
     *
     * @param menus 菜单列表
     * @return 路由列表
     */
    @Override
    public List<RouterBo> buildMenus(List<Menu> menus) {
        List<RouterBo> routers = new LinkedList<>();
        for (Menu menu : menus) {
            RouterBo router = new RouterBo();
            router.setParentId(menu.getParentId());
            router.setHidden(menu.getVisible() == 2);
            router.setName(getRouteName(menu));
            router.setPath(getRouterPath(menu));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaBo(menu.getName(), menu.getIcon(), menu.getIsCache() == 2));
            List<Menu> cMenus = menu.getChildren();
            if (!cMenus.isEmpty() && cMenus.size() > 0 && EMenuType.M.getCode() == menu.getType()) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildMenus(cMenus));
            } else if (isMeunFrame(menu)) {
                List<RouterBo> childrenList = new ArrayList<>();
                RouterBo children = new RouterBo();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StringUtil.capitalize(menu.getName()));
                children.setMeta(new MetaBo(menu.getName(), menu.getIcon(), menu.getIsCache() == 2));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }

    /**
     * 获取路由名称
     *
     * @param menu 菜单信息
     * @return 路由名称
     */
    public String getRouteName(Menu menu) {
        String routerName = StringUtil.capitalize(menu.getName());
        // 非外链并且是一级目录（类型为目录）
        if (isMeunFrame(menu)) {
            routerName = StringUtil.EMPTY;
        }
        return routerName;
    }

    /**
     * 是否为菜单内部跳转
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isMeunFrame(Menu menu) {
        return menu.getParentId().intValue() == 0 && EMenuType.C.getCode() == menu.getType()
            && menu.getIsFrame() == EMenuIsFrame.NO.getCode();
    }

    /**
     * 获取路由地址
     *
     * @param menu 菜单信息
     * @return 路由地址
     */
    public String getRouterPath(Menu menu) {
        String routerPath = menu.getPath();
        // 非外链并且是一级目录（类型为目录）
        if (0 == menu.getParentId().intValue() && EMenuType.M.getCode() == menu.getType()
            && EMenuIsFrame.NO.getCode() == menu.getIsFrame()) {
            routerPath = "/" + menu.getPath();
        }
        // 非外链并且是一级目录（类型为菜单）
        else if (isMeunFrame(menu)) {
            routerPath = "/";
        }
        return routerPath;
    }

    /**
     * 获取组件信息
     *
     * @param menu 菜单信息
     * @return 组件信息
     */
    public String getComponent(Menu menu) {
        String component = Constant.LAYOUT;
        if (StringUtil.isNotBlank(menu.getComponent()) && !isMeunFrame(menu)) {
            component = menu.getComponent();
        }
        return component;
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public Set<String> getMenuPermsByUserId(Integer userId) {
        return menuDaoImpl.getMenuPermsByUserId(userId);
    }

    /**
     * 根据用户查询系统菜单列表
     *
     * @param menu   菜单信息
     * @param id 用户ID
     * @return 菜单列表
     */
    @Override
    public List<Menu> selectMenuList(MenuVo menu) {
        menu.setEnvironment(EEnvironmentUserType.WEB.getCode());
        List<Menu> menuList = null;
        // 管理员显示所有菜单信息
        if (TokenUtil.isAdmin(menu.getUserId())) {
            menu.setUserId(null);
            menuList = menuDaoImpl.selectMenuList(menu);
        } else {
            menuList = menuDaoImpl.selectMenuListByUserId(menu);
        }
        return menuList;
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param menus 菜单列表
     * @return 下拉树结构列表
     */
    @Override
    public List<TreeSelect> buildMenuTreeSelect(List<Menu> menus) {
        List<Menu> menuTrees = buildMenuTree(menus);
        return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    @Override
    public List<Menu> buildMenuTree(List<Menu> menus) {
        List<Menu> returnList = new ArrayList<>();
        List<Integer> tempList = new ArrayList<>();
        for (Menu dept : menus) {
            tempList.add(dept.getId());
        }
        for (Iterator<Menu> iterator = menus.iterator(); iterator.hasNext();) {
            Menu menu = iterator.next();
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(menu.getParentId())) {
                recursionFn(menus, menu);
                returnList.add(menu);
            }
        }
        if (returnList.isEmpty()) {
            returnList = menus;
        }
        return returnList;
    }

    /**
     * 根据用户查询系统菜单列表
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    @Override
    public List<Menu> selectMenuList(Integer userId) {
        MenuVo menu = new MenuVo();
        menu.setUserId(userId);
        return selectMenuList(menu);
    }

    /**
     * 根据角色ID查询菜单树信息
     *
     * @param roleId 角色ID
     * @return 选中菜单列表
     */
    @Override
    public List<Integer> selectMenuListByRoleId(Integer roleId) {
        Role role = roleDao.findById(roleId);
        return menuDaoImpl.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly());
    }

    /**
     * 查询菜单信息
     * 
     * @param menu
     * @return
     */
    @Override
    public List<Menu> list(MenuVo menu) {
        return menuDaoImpl.list(menu);
    }

}